package com.abel.bigwater.data.jnrq

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.DataException
import com.abel.bigwater.data.MsgHelper
import com.abel.bigwater.data.NotEnoughDataException
import com.abel.bigwater.msg.RtuMsg
import com.abel.bigwater.msg.RtuMsgType
import com.alibaba.fastjson.JSON
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import org.slf4j.LoggerFactory
import java.net.InetSocketAddress
import java.util.*

class JnrqDecoder(val _firmId: String,
                  val _firmCode: String,
                  val msgHelper: MsgHelper?) : ByteToMessageDecoder() {
    /**
     * Decode the from one [ByteBuf] to an other. This method will be called till either the input
     * [ByteBuf] has nothing to read when return from this method or till nothing was read from the input
     * [ByteBuf].
     *
     * @param ctx           the [ChannelHandlerContext] which this [ByteToMessageDecoder] belongs to
     * @param in            the [ByteBuf] from which to read data
     * @param out           the [List] to which decoded messages should be added
     * @throws Exception    is thrown if an error occurs
     */
    override fun decode(ctx: ChannelHandlerContext?, `in`: ByteBuf?, out: MutableList<Any>?) {
        `in` ?: return
        lgr.info("收到消息 ${ctx?.channel()?.remoteAddress()}:\n${BcdString.hexString(`in`.duplicate(), null, true)}")

        while (`in`.readableBytes() > 0) {
            val p1 = JnrqPacket.splitPacket(`in`)
            if (p1 != null) {
                msgHelper?.sendRawPacket(RtuMsg().apply {
                    station = p1.devAddrInt?.toString()
                    rawBinary = p1.rawBinary
                    msgType = RtuMsgType.DATA_BIN.name
                    rawHex = BcdString.hexString(p1.rawBinary!!)

                    source = ctx!!.channel().remoteAddress().toString()
                    target = ctx.channel().localAddress().toString()
                    serverPort = (ctx.channel().localAddress() as InetSocketAddress?)?.port

                    firmId = _firmId
                    firmCode = _firmCode
                    recvTime = Date()
                })
            } else {
                break
            }

            try {
                out!!.add(JnrqPacket.parseBuffer(`in`, p1.skipCnt))
            } catch (ex: NotEnoughDataException) {
                // just ignore, wait for next receiving
                break
            } catch (ex: Exception) {
                lgr.error("fail to parse jnrq-packet caused by {}", ex.message)
                lgr.error(ex.message, ex)
                break
            }

            Thread.yield()
        }

        lgr.info("收到瑞泉消息 ${out?.size} 条: ${JSON.toJSONString(out, true)}")
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(JnrqDecoder::class.java)
    }
}